package tips.p_1000.p51_100;

/**
 * 使用下面描述的算法可以扰乱字符串 s 得到字符串 t ：
 * 如果字符串的长度为 1 ，算法停止
 * 如果字符串的长度 > 1 ，执行下述步骤：
 * 在一个随机下标处将字符串分割成两个非空的子字符串。
 * 即，如果已知字符串 s ，则可以将其分成两个子字符串 x 和 y ，且满足 s = x + y 。
 * 随机 决定是要「交换两个子字符串」还是要「保持这两个子字符串的顺序不变」。
 * 即，在执行这一步骤之后，s 可能是 s = x + y 或者 s = y + x 。
 * 在 x 和 y 这两个子字符串上继续从步骤 1 开始递归执行此算法。
 * 给你两个 长度相等 的字符串 s1 和 s2，判断 s2 是否是 s1 的扰乱字符串。如果是，返回 true ；否则，返回 false 。
 * <p>
 * 示例 1：
 * 输入：s1 = "great", s2 = "rgeat"
 * 输出：true
 * 解释：s1 上可能发生的一种情形是：
 * "great" --> "gr/eat" // 在一个随机下标处分割得到两个子字符串
 * "gr/eat" --> "gr/eat" // 随机决定：「保持这两个子字符串的顺序不变」
 * "gr/eat" --> "g/r / e/at" // 在子字符串上递归执行此算法。两个子字符串分别在随机下标处进行一轮分割
 * "g/r / e/at" --> "r/g / e/at" // 随机决定：第一组「交换两个子字符串」，第二组「保持这两个子字符串的顺序不变」
 * "r/g / e/at" --> "r/g / e/ a/t" // 继续递归执行此算法，将 "at" 分割得到 "a/t"
 * "r/g / e/ a/t" --> "r/g / e/ a/t" // 随机决定：「保持这两个子字符串的顺序不变」
 * 算法终止，结果字符串和 s2 相同，都是 "rgeat"
 * 这是一种能够扰乱 s1 得到 s2 的情形，可以认为 s2 是 s1 的扰乱字符串，返回 true
 * <p>
 * 示例 2：
 * 输入：s1 = "abcde", s2 = "caebd"
 * 输出：false
 * <p>
 * 示例 3：
 * 输入：s1 = "a", s2 = "a"
 * 输出：true
 * <p>
 * 提示：
 * s1.length == s2.length
 * 1 <= s1.length <= 30
 * s1 和 s2 由小写英文字母组成
 *
 * @author hc
 */
public class Demo87 {

    /**
     * 递归 时间复杂度 O(5^n)
     */
    public boolean isScramble(String s1, String s2) {
        if (s1.equals(s2)) {
            return true;
        }
        if (!check(s1, s2)) {
            return false;
        }

        int len = s1.length();
        for (int i = 1; i < len; i++) {
            String s1_1 = s1.substring(0, i), s1_2 = s1.substring(i);
            String s2_1 = s2.substring(0, i), s2_2 = s2.substring(i);
            if (isScramble(s1_1, s2_1) && isScramble(s1_2, s2_2)) {
                return true;
            }

            String s2_3 = s2.substring(0, len - i), s2_4 = s2.substring(len - i);
            if (isScramble(s1_1, s2_3) && isScramble(s1_2, s2_4)) {
                return true;
            }
        }
        return false;
    }

    /**
     * check s1, s2 frequency
     */
    private boolean check(String s1, String s2) {
        if (s1.length() != s2.length()) {
            return false;
        }
        int len = s1.length();
        int[] cnt1 = new int[26], cnt2 = new int[26];
        for (int i = 0; i < len; i++) {
            ++cnt1[s1.charAt(i) - 'a'];
            ++cnt2[s2.charAt(i) - 'a'];
        }
        for (int i = 0; i < 26; i++) {
            if (cnt1[i] != cnt2[i]) {
                return false;
            }
        }
        return true;
    }

    /**
     * 递归 + 记忆化搜索 时间复杂度 O(n^4)
     */
    String s1, s2;
    int[][][] cache;
    int N = -1, Y = 1, EMPTY = 0;

    public boolean isScramble2(String s1, String s2) {

        this.s1 = s1;
        this.s2 = s2;
        if (s1.equals(s2)) {
            return true;
        }
        if (s1.length() != s2.length()) {
            return false;
        }
        int len = s1.length();
        // 默认为 EMPTY
        // 前两位都是下标，后一位是长度，所以需要开辟n+1的空间
        cache = new int[len][len][len + 1];
        return dsf(0, 0, len);
    }

    private boolean dsf(int i, int j, int len) {
        if (cache[i][j][len] != EMPTY) {
            return cache[i][j][len] == Y;
        }
        // 取出 s1 从 i 开始后面的 len 位 和 s2 从 j 开始后面的 len 位
        String s1_1 = s1.substring(i, i + len), s2_1 = s2.substring(j, j + len);
        if (s1_1.equals(s2_1)) {
            cache[i][j][len] = Y;
            return true;
        }
        if (!check(s1_1, s2_1)) {
            cache[i][j][len] = N;
            return false;
        }

        for (int k = 1; k < len; k++) {

            if (dsf(i, j, k) && dsf(i + k, j + k, len - k)) {
                cache[i][j][len] = Y;
                return true;
            }

            if (dsf(i, len - k + j, k) && dsf(i + k, j, len - k)) {
                cache[i][j][len] = Y;
                return true;
            }
        }

        cache[i][j][len] = N;
        return false;
    }

    public static void main(String[] args) {
        System.out.println(new Demo87().isScramble2("abcde", "caebd"));
        System.out.println(new Demo87().isScramble2("a", "a"));
    }
}
